package com.nirvana.generate;

import com.nirvana.config.PluginConfig;
import com.nirvana.data.DependenceData;
import com.nirvana.factory.FlatBuffersResolveFactory;
import com.nirvana.factory.ItemFactory;
import com.nirvana.flatbuffers.IResolve;
import com.nirvana.item.CommonItem;
import com.nirvana.item.DefPropertyItem;
import com.nirvana.type.ItemType;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;

import static com.nirvana.config.PluginConfig.PLUGIN_GENERATE_FLAG;

public class DefFbWrapperClassGenerator implements GenerateInterface {
    private static DefFbWrapperClassGenerator INSTANCE;
    private final CommonItem commonItem = ItemFactory.getCommonItem(ItemType.DefPropertyItem);

    private DefFbWrapperClassGenerator() {}

    public static DefFbWrapperClassGenerator getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new DefFbWrapperClassGenerator();
        }
        return INSTANCE;
    }

    @Override
    public void generate() {
        StringBuilder content = new StringBuilder();
        StringBuilder packageContent = new StringBuilder();
        StringBuilder importContent = new StringBuilder();
        StringBuilder classContent = new StringBuilder();
        StringBuilder fieldContent = new StringBuilder();

        Set<String> hasImport = new HashSet<>();

        generatePackage(packageContent);
        commonItem.itemList.forEach(item -> {
            if (item instanceof DefPropertyItem defPropertyItem) {
                generateImport(importContent, defPropertyItem, hasImport);
                generateField(fieldContent, defPropertyItem);
            }
        });

        if (PluginConfig.getInstance().extendsGroupDef()) {
            fieldContent.append("""
                        def openGroup: Array[Int] = {
                            val n = raw.openGroupLength
                            if (n == 0) Array.empty
                            else Array.tabulate(n) { i => raw.openGroup(i) }
                        }
                        def offGroup: Array[Int] = {
                            val n = raw.offGroupLength
                            if (n == 0) Array.empty
                            else Array.tabulate(n) { i => raw.offGroup(i) }
                        }
                    """);
        }

        generateClass(classContent, fieldContent);

        String defName = commonItem.textField.getText();
        content
                .append(packageContent)
                .append(importContent)
                .append("""
                        import com.nirvana.bean.flatBuffers.ImplicitConvert._
                        import scala.collection.mutable.ArrayBuffer
                        """)
                .append(classContent);

        String flatBuffersBasePath = PluginConfig.getInstance().flatBuffersBasePath();
        Path basePath = Paths.get(flatBuffersBasePath);
        try {
            if (Files.notExists(basePath)) {
                Files.createDirectories(basePath);
            }
            Path path = Paths.get(flatBuffersBasePath + "/" + defName + "FbWrapper.scala");
            Files.writeString(path, content.toString());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void generatePackage(StringBuilder packageContent) {
        packageContent.append("""
                package %s
                
                """.formatted(PluginConfig.getInstance().flatBuffersDotPackage()));
    }

    private void generateImport(StringBuilder importContent, DefPropertyItem item, Set<String> hasImport) {
        if (!item.isResolve()) return;

        String propertyType = item.getPropertyType();
        String[] types = propertyType.replaceAll("[^a-zA-Z0-9]", " ").split("\\s+");
        for (String type : types) {
            if (hasImport.contains(type) || StringUtils.isBlank(type)) continue;

            DependenceData dependenceDataByType = PluginConfig.getInstance().getDependenceDataByType(type);

            if (dependenceDataByType == null) continue;

            // 导入依赖类
            if (dependenceDataByType.canImport()) {
                importContent.append("""
                import %s
                """.formatted(dependenceDataByType.getFullClassName()));
            }
            hasImport.add(type);
        }

    }

    private void generateClass(StringBuilder classContent, StringBuilder fieldContent) {
        String defName = commonItem.textField.getText();
        classContent.append("""
                
                final class %sFbWrapper(private val raw: %sFb) extends %sBase {
                %s
                }
                %s
                """.formatted(
                defName,
                defName,
                defName,
                fieldContent.toString(),
                PLUGIN_GENERATE_FLAG
        ));
    }

    private void generateField(StringBuilder fieldContent, DefPropertyItem item) {
        String defName = commonItem.textField.getText();
        String propertyType = item.getPropertyType();
        String propertyName = item.getPropertyName();
        String comment = item.getComment();

        IResolve resolver = FlatBuffersResolveFactory.getResolver(propertyType);
        if (resolver == null) return;

        resolver.resolveFbWrapperField(fieldContent, defName, comment, propertyName, propertyType);
    }
}
